﻿using System;
using System.Diagnostics.Contracts;
using CardPlay.Model.Primitives;

namespace CardPlay.Model.GameModel.Presentation
{
    public static class CardArrangement
    {
        public static Size[] StackTower(CardPile pile, LayoutMetrics metrics)
        {
            Contract.Requires<ArgumentNullException>(pile != null, "pile");
            
            var stackingOffset = new Size(2, 1);
// ReSharper disable PossibleNullReferenceException
            var points = new Size[pile.Count];
// ReSharper restore PossibleNullReferenceException
            
            for (var i = 1; i < pile.Count; ++i)
            {
                var offset = i % 10 == 0 ? stackingOffset : Size.Empty;
                points[i] = points[i - 1] + offset;
            }
            
            return points;
        }

        public static Size[] StackWaste(CardPile pile, LayoutMetrics metrics)
        {
            return SetStackedCardPlacement(StackTower(pile, metrics), pile, pile.StackLimit + 1, 
                new Size(metrics.StackingOffsetOnFaceUpCard.Width, 1),
                new Size(metrics.StackingOffsetOnFaceDownCard.Width, 1));
        }
    
        public static Size[] StackSquare(CardPile pile, LayoutMetrics metrics)
        {
            return GetStackedCardPlacement(pile, Size.Empty, Size.Empty);
        }

        public static Size[] StackLeftToRight(CardPile pile, LayoutMetrics metrics)
        {
            return GetStackedCardPlacement(pile, 
                new Size(metrics.StackingOffsetOnFaceUpCard.Width, 0),
                new Size(metrics.StackingOffsetOnFaceDownCard.Width, 0));
        }

        public static Size[] StackRightToLeft(CardPile pile, LayoutMetrics metrics)
        {
            return GetStackedCardPlacement(pile, 
                new Size(-metrics.StackingOffsetOnFaceUpCard.Width, 0),
                new Size(-metrics.StackingOffsetOnFaceDownCard.Width, 0));
        }
        
        public static Size[] StackTopToBottom(CardPile pile, LayoutMetrics metrics)
        {
            return GetStackedCardPlacement(pile, 
                new Size(0, metrics.StackingOffsetOnFaceUpCard.Height),
                new Size(0, metrics.StackingOffsetOnFaceDownCard.Height));
        }

        public static Size[] StackBottomToTop(CardPile pile, LayoutMetrics metrics)
        {
            return GetStackedCardPlacement(pile, 
                new Size(0, -metrics.StackingOffsetOnFaceUpCard.Height),
                new Size(0, -metrics.StackingOffsetOnFaceDownCard.Height));
        }
        
        public static Size[] StackUniformlyTopToBottom(CardPile pile, LayoutMetrics metrics)
        {
            return GetStackedCardPlacement(pile, 
                new Size(0, metrics.StackingOffsetOnFaceUpCard.Height),
                new Size(0, metrics.StackingOffsetOnFaceUpCard.Height));
        }

        public static Size[] GetStackedCardPlacement(CardPile pile, 
            Size stackingOffsetOnFaceUpCard,
            Size stackingOffsetOnFaceDownCard)
        {
            Contract.Requires<ArgumentNullException>(pile != null, "pile");

// ReSharper disable PossibleNullReferenceException
            return SetStackedCardPlacement(new Size[pile.Count], pile, 1,
// ReSharper restore PossibleNullReferenceException
                stackingOffsetOnFaceUpCard, 
                stackingOffsetOnFaceDownCard);
        }

        public static Size[] SetStackedCardPlacement(Size[] points, CardPile pile, int startIndex,
            Size stackingOffsetOnFaceUpCard,
            Size stackingOffsetOnFaceDownCard)
        {
            Contract.Requires<ArgumentNullException>(pile != null, "pile");
            Contract.Requires<ArgumentOutOfRangeException>(startIndex > 0, "startIndex");
            
// ReSharper disable PossibleNullReferenceException
            for (var i = startIndex; i < pile.Count; ++i)
// ReSharper restore PossibleNullReferenceException
            {
                var offset = pile.CardAt(i - 1).IsFaceUp ? 
                    stackingOffsetOnFaceUpCard :
                    stackingOffsetOnFaceDownCard;
            
                points[i] = points[i - 1] + offset;
            }
            
            return points;
        }
    }
}
